
import json
import asyncio
import base64
import re
from channels.generic.websocket import AsyncWebsocketConsumer
from datetime import datetime
import time
import uuid

# 用于存储每个聊天室的消息历史
chat_history = {}  # 格式: {'shop_1001': [message1, message2, ...]}
# 每个聊天室最多保存的消息数量
MAX_HISTORY_LENGTH = 50
# 消息定时器，保存需要定时删除的消息
message_timers = {}  # 格式: {'msg_id': {'expireAt': timestamp, 'shop_id': 'shop_1001'}}


class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        # 获取URL中的shop_id参数（可能是中文）
        self.shop_id = self.scope['url_route']['kwargs']['shop_id']

        # 为调试添加日志
        print(f"尝试连接到聊天室，原始shop_id: {self.shop_id}")

        # 获取安全的群组名
        safe_group_name = self.get_safe_group_name(f'chat_shop_{self.shop_id}')
        self.room_group_name = safe_group_name

        # 为调试添加日志
        print(f"使用安全的群组名: {self.room_group_name}")

        # 加入房间
        try:
            await self.channel_layer.group_add(
                self.room_group_name,
                self.channel_name
            )
            # 连接成功
            print(f"成功加入群组: {self.room_group_name}")
            await self.accept()

            # 发送历史消息 - 过滤掉已过期的消息
            if self.shop_id in chat_history and chat_history[self.shop_id]:
                now = time.time()
                valid_messages = []

                for msg in chat_history[self.shop_id]:
                    # 检查是否有过期时间且是否已过期
                    message_id = msg.get('messageId')
                    if message_id and message_id in message_timers:
                        if message_timers[message_id]['expireAt'] <= now:
                            # 消息已过期，不发送
                            continue
                    valid_messages.append(msg)

                # 更新聊天历史，删除过期消息
                chat_history[self.shop_id] = valid_messages

                await self.send(text_data=json.dumps({
                    'type': 'history',
                    'messages': valid_messages
                }))
        except Exception as e:
            print(f"连接到群组时出错: {str(e)}")
            # 即使出错，也尝试接受连接
            await self.accept()
            # 通知客户端发生了错误
            await self.send(text_data=json.dumps({
                'type': 'error',
                'message': f"连接错误: {str(e)}"
            }))

    def get_safe_group_name(self, name):
        # 检查名称是否已经符合要求
        if re.match(r'^[a-zA-Z0-9\-_\.]+$', name) and len(name) < 100:
            return name

        # 使用base64编码确保安全
        encoded_name = base64.urlsafe_b64encode(name.encode('utf-8')).decode('ascii')
        # 移除填充字符 '='
        encoded_name = encoded_name.replace('=', '')

        # 确保长度小于100
        if len(encoded_name) >= 100:
            encoded_name = encoded_name[:99]

        return encoded_name

    async def disconnect(self, close_code):
        # 离开房间组
        try:
            await self.channel_layer.group_discard(
                self.room_group_name,
                self.channel_name
            )
            print(f"已断开与群组的连接: {self.room_group_name}")
        except Exception as e:
            print(f"断开连接时出错: {str(e)}")

    # 从WebSocket接收消息
    async def receive(self, text_data):
        try:
            text_data_json = json.loads(text_data)

            # 检查是否是删除消息的指令
            if text_data_json.get('type') == 'delete_message':
                message_id = text_data_json.get('messageId')
                if message_id:
                    await self.handle_delete_message(message_id)
                    return

            message = text_data_json.get('message', '')
            username = text_data_json.get('username', 'Anonymous')
            # 获取加密标记
            encrypted = text_data_json.get('encrypted', False)
            # 获取消息ID
            message_id = text_data_json.get('messageId') or f"msg_{uuid.uuid4().hex[:8]}"
            # 获取过期时间（秒）
            expire_after = text_data_json.get('expireAfter', 0)

            timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            # 保存消息到内存
            message_data = {
                'username': username,
                'message': message,
                'timestamp': timestamp,
                'encrypted': encrypted,  # 保存加密标记
                'messageId': message_id,  # 添加消息ID
                'expireAfter': expire_after  # 添加过期时间
            }

            # 确保该店铺的历史记录列表存在
            if self.shop_id not in chat_history:
                chat_history[self.shop_id] = []

            # 添加消息到历史记录并限制长度
            chat_history[self.shop_id].append(message_data)
            if len(chat_history[self.shop_id]) > MAX_HISTORY_LENGTH:
                chat_history[self.shop_id] = chat_history[self.shop_id][-MAX_HISTORY_LENGTH:]

            # 如果有过期时间，设置定时器
            if expire_after > 0:
                expire_at = time.time() + expire_after
                message_timers[message_id] = {
                    'expireAt': expire_at,
                    'shop_id': self.shop_id
                }

                # 创建异步任务来处理消息过期
                asyncio.create_task(self.schedule_message_deletion(message_id, expire_after))

            # 发送消息到房间
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'chat_message',
                    'message': message,
                    'username': username,
                    'timestamp': timestamp,
                    'encrypted': encrypted,  # 传递加密标记
                    'messageId': message_id,  # 传递消息ID
                    'expireAfter': expire_after  # 传递过期时间
                }
            )
        except Exception as e:
            print(f"处理消息时出错: {str(e)}")
            # 通知客户端发生了错误
            await self.send(text_data=json.dumps({
                'type': 'error',
                'message': f"消息处理错误: {str(e)}"
            }))

    # 安排消息删除
    async def schedule_message_deletion(self, message_id, delay_seconds):
        # 等待指定的秒数
        await asyncio.sleep(delay_seconds)

        # 检查消息是否仍然存在
        if message_id in message_timers:
            # 删除消息
            await self.handle_delete_message(message_id)

    # 处理删除消息
    async def handle_delete_message(self, message_id):
        try:
            # 从消息定时器中删除
            if message_id in message_timers:
                shop_id = message_timers[message_id]['shop_id']
                del message_timers[message_id]

                # 从聊天历史中删除
                if shop_id in chat_history:
                    chat_history[shop_id] = [msg for msg in chat_history[shop_id] if msg.get('messageId') != message_id]

                # 获取安全的群组名
                safe_group_name = self.get_safe_group_name(f'chat_shop_{shop_id}')

                # 通知所有连接的客户端删除此消息
                await self.channel_layer.group_send(
                    safe_group_name,
                    {
                        'type': 'delete_message_notification',
                        'messageId': message_id
                    }
                )
        except Exception as e:
            print(f"删除消息时出错: {str(e)}")

    # 发送删除消息通知
    async def delete_message_notification(self, event):
        message_id = event['messageId']

        # 发送删除通知到WebSocket
        await self.send(text_data=json.dumps({
            'type': 'delete_message',
            'messageId': message_id
        }))

    # 在发送消息时包含加密标记和过期时间信息
    async def chat_message(self, event):
        message = event['message']
        username = event['username']
        timestamp = event.get('timestamp', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        encrypted = event.get('encrypted', False)  # 获取加密标记
        message_id = event.get('messageId', None)  # 获取消息ID
        expire_after = event.get('expireAfter', 0)  # 获取过期时间

        # 发送消息到WebSocket
        await self.send(text_data=json.dumps({
            'type': 'message',
            'message': message,
            'username': username,
            'timestamp': timestamp,
            'encrypted': encrypted,  # 包含加密标记
            'messageId': message_id,  # 包含消息ID
            'expireAfter': expire_after  # 包含过期时间
        }))